package uebung02;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;

import tools.Arrow;
import tools.ColorConversions;
import tools.Derivative;
import tools.Gauss;

public class Aufgabe03 {
	static int count = 2000;
	public static void main(String[] args) {
		if (args.length!=0) {
			if (args[0].toUpperCase().equals("showTools".toUpperCase())) {
				try {
					Derivative.main(new String[0]);
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
			if (args[0].toUpperCase().equals("pickPixels".toUpperCase())) {
				try {
					PickingTheRightPixels.main(new String[0]);
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
		} else 
		try {
			final BufferedImage kermit = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("res/kermit001.jpg"));
			final int w = kermit.getWidth(), h = kermit.getHeight(), w2 = w/2, h2 = h/2;
			int n = 16;

			
			int[] pixels = kermit.getRGB(0, 0, w, h, new int[w*h], 0, w);
			int[] lum = ColorConversions.getFastLuminance(pixels, new int[pixels.length]);
			
			int[] dx = Derivative.dx(lum, w, h, new int[lum.length]);
			int[] dy = Derivative.dy(lum, w, h, new int[lum.length]);
			
			double[] lengths = Derivative.getLengths(dx, dy, new double[dx.length]);
			double[] angles = Derivative.getAngles(dx, dy, new double[dx.length]);

			double theta = Math.toRadians(0);
			
			final AffineTransform transl = AffineTransform.getTranslateInstance(w2, h2);
			
			final AffineTransform at2 = AffineTransform.getScaleInstance(1, 1);
//			at2.concatenate(AffineTransform.getTranslateInstance(1, 1));
			at2.concatenate(AffineTransform.getRotateInstance(theta));
			
			final AffineTransform at = new AffineTransform();

			final ArrayList<double[]> descriptor = new ArrayList<double[]>();
			final double[] ang = new double[8];

			final Rectangle2D selection = new Rectangle2D.Double();
			
			for (int i = 0; i<ang.length;i++) {
				ang[i] = ((i*2*Math.PI))/8-Math.PI;
			}
			Point2D centerD = new Point2D.Double();

			final double maxDist = Math.sqrt(128);
			
			for (int x = -2; x<2; x++) {
				for (int y = -2; y<2; y++) {
					selection.setFrame(x*4, y*4, 4, 4);
					Shape transSelection = transl.createTransformedShape(at2.createTransformedShape(selection));
					Rectangle bounds = transSelection.getBounds();
					
					double[] histo = new double[8];
					
					Point2D centerH = new Point2D.Double(selection.getCenterX(), selection.getCenterY());
					Point2D p = new Point2D.Double();
					
					centerH = at2.transform(centerH, centerH);
					
					for (int j = (int)Math.floor(bounds.getMinY()), endy = (int)Math.ceil(bounds.getMaxY()); j<endy; j++) {
						for (int i = (int)Math.floor(bounds.getMinX()), endx = (int)Math.ceil(bounds.getMaxX()); i<endx; i++) {
//							System.out.println(i+" "+j);
							p.setLocation(i,j);
							try {
								p = transl.inverseTransform(p, p);
								p = at2.inverseTransform(p, p);
							}
							catch (NoninvertibleTransformException nte) {
								nte.printStackTrace();
							}
//							System.out.println(p);
							
							if (selection.contains(p)) {
								//calculating histogram values
								for (int l=0; l<8; l++) {
									double g = Gauss.gaussian(p.getX()-centerD.getX(), p.getY()-centerD.getY(), 8);
									double f = Math.abs(1-p.distance(centerH)/maxDist); 

//									System.out.println(i+"  "+j);
									
									double omega = l*45.+Math.toDegrees(theta)-Math.toDegrees(angles[i+j*w]);
									while (omega<0)
										omega += 360;
									while (omega >360)
										omega -= 360;
									double et = 1-(Math.abs(omega/45.));
									double e = Math.max(0, et)*lengths[i+j*w];

									double value = g*f*e;
//									System.out.println("gauss: "+g+" f: "+f+" e: "+e+" et: "+et+" "+lengths[i+j*w]);
									histo[l]+=value;
								}
							}
						}
					}		

					descriptor.add(histo);
				}
			}
			
			double max = 0;
			double sum = 0;
			//normalisation
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					max = Math.max(bubu[j], max);
					sum += bubu[j]*bubu[j];
				}
			}
			sum = Math.sqrt(sum);
			//cap  
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					bubu[j] = Math.min(bubu[j]/sum,0.2);
				}
			}
			//renormalization
			max = 0;
			sum = 0;
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					sum += bubu[j]*bubu[j];
				}
			}
			sum = Math.sqrt(sum);
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					bubu[j] = bubu[j]/sum;
				}	
			}
			max = 0;
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					max = Math.max(bubu[j], max);
				}
			}
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					System.out.print(bubu[j]+" ");
				}
			}
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					bubu[j] /= max/100;
				}
			}
		
			max = 0;
			for (int j = 0; j < lengths.length; j++) {
				max = Math.max(max, lengths[j]);
			}
			for (int j = 0; j < lengths.length; j++) {
				lengths[j] = 255.*(lengths[j]/max);
			}
			Derivative.prepareLengths(lengths, lum);
			pixels = ColorConversions.buildRGB(lum, lum, lum, pixels);
			kermit.setRGB(0, 0, w, h, pixels, 0, n);

			final BufferedImage bi = kermit.getSubimage(w2, h2, n, n);

			JFrame ff = new JFrame();
			ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			Point2D a = new Point2D.Double(100,100), b = new Point2D.Double(500,300);
			
			double arrowWidth = 10, arrowHeight = 40;
			
			final Arrow arrw = new Arrow();
			arrw.setArrow(a, b, arrowWidth, arrowHeight);
			final AffineTransform translator = new AffineTransform();
			
			final AffineTransform rotat0r = new AffineTransform();
			final Point2D centre = new Point2D.Double(100,100);
			final JComponent canvas = new JComponent() {
				{setPreferredSize(new Dimension(800,800));}
				Line2D line = new Line2D.Double();
				@Override
				public void paint(Graphics g) {
					Graphics2D g2 = (Graphics2D)g;
					g2.setStroke(new BasicStroke(0.3f));
				    g2.transform(at);
				   
				    g2.setColor(Color.white);
			    	g2.fillRect(0, 0, this.getWidth(), this.getHeight());

					g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				    g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
				    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
				    
				    Point2D point = new Point2D.Double();
				    Point2D centransform = new Point2D.Double();
				    
				    for (int j = 0; j < Math.min(descriptor.size(), count); j++) {
				    	double shiftx = (j%4)*200, shifty = (j/4)*200; 
				    	
				    	line.setLine(0, shifty, 800, shifty);
				    	g2.setColor(Color.cyan);
				    	if (Double.compare(shifty, 0) != 0)
				    		g2.draw(line);
				    	line.setLine(shiftx, 0, shiftx, 800);
				    	if (Double.compare(shiftx, 0) != 0)
				    		g2.draw(line);
				    	double[] histo = descriptor.get(j);
					    for (int i = 0; i < histo.length; i++) {
							point.setLocation(1,0);
							double rot = Math.toRadians(i*45);

							rotat0r.setToRotation(rot);
							rotat0r.transform(point, point);

							point.setLocation(histo[i]*point.getX()+centre.getX()+shiftx, histo[i]*point.getY()+centre.getY()+shifty);

							centransform = new Point2D.Double(centre.getX()+shiftx, centre.getY()+shifty);
							if (Double.compare(point.distanceSq(centransform),0)!=0) {
								arrw.setArrow(centransform, point, 3, 10);
							    g2.setColor(Color.magenta);
								g2.fill(arrw);
								g2.setColor(Color.black);
								g2.draw(arrw);
							}
							else {
								point.setLocation(1,0);
								rotat0r.transform(point, point);
								point.setLocation(point.getX()+shiftx+centre.getX(), point.getY()+shifty+centre.getY());
								arrw.setArrow(centransform, point, 3, 10);
							    g2.setColor(Color.green);
								g2.fill(arrw);
								g2.setColor(Color.black);
								g2.draw(arrw);
							}
						}
						
					}
				    
					super.paint(g);
				}
			};
			canvas.addMouseWheelListener(new MouseWheelListener() {
				
				@Override
				public void mouseWheelMoved(MouseWheelEvent e) {
					
					double scale = Math.pow(1.01, e.getWheelRotation());

					try {
						Point2D p = new Point2D.Double(e.getX(), e.getY()); 
						at.inverseTransform(p, p);
						at.concatenate(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
						at.concatenate(AffineTransform.getScaleInstance(scale, scale));
						at.concatenate(AffineTransform.getTranslateInstance(-p.getX(), -p.getY()));
					}
					catch (NoninvertibleTransformException nte ) {
						nte.printStackTrace();
					}
					canvas.repaint();
				}
			});
			ff.add(canvas);
			ff.pack();
			
			ff.setVisible(true);	
//			BufferedImage aufgabe04 = new BufferedImage(canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_ARGB);
//			try {
//				canvas.paint(aufgabe04.getGraphics());
//			}
//			catch (Exception e) {
//				e.printStackTrace();
//			}
//			ImageIO.write(aufgabe04, "PNG", new File("SIFT03.png"));

		}
		catch (IOException iox) {
			iox.printStackTrace();
		}
	}
}
